package marketplace_server;

import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;

import java.util.Iterator;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import marketplace_bankserver.BankClient;
import marketplace_client.Client;

/**
 *
 * @author gianmariospacagna
 */
public class ServerImpl extends UnicastRemoteObject implements Server {

    // A map that keeps track of logged users in the system, prevents duplicate names
    private HashMap<String, Client> userMap = new HashMap<String, Client>();
    // A map that keeps track of what priceRanges each client is interested in
    private HashMap<Integer, Client> requestedPriceRanges = new HashMap<Integer, Client>();
    // A list of all the registered clients in the system 
    
    private BankClient bankClient;
    private SQLiteManager SQLManager;

    public static void main(String[] args) {

        try {

            ServerImpl market = new ServerImpl();
            Registry r = LocateRegistry.createRegistry(1099);
            r.bind(ServerImpl.class.getSimpleName(), market);
            System.out.println("The server is running");

            new ServerMenu(market).setVisible(true);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public ServerImpl() throws RemoteException {
        super();
        SQLManager = new SQLiteManager();


    }

    private void broadcastItemsThatBelongToRequestedPriceRanges() throws RemoteException, SQLException {

        // The requested price ranges should only have unique clients
        for (Integer price : requestedPriceRanges.keySet()) {
            // The current client in the set that we're iterating
            Client client = requestedPriceRanges.get(price);
            // Allocating a list of items where we'll put the items that match
            // the current clients price range
            ArrayList<Item> items = new ArrayList<Item>();
            
            ArrayList<Item> availableItems=SQLManager.getItems();
            
            for (Item i : availableItems) {
                // When we find an item that has a price that's within
                // the clients range we'll add it to our list of items
                if (i.getPrice().intValue() <= price.intValue()) {
                    items.add(i);
                }
            }
            // Once we've filled the list with matching items
            // We'll ship it off to the designated client
            client.serverHasItemInMyPriceRange(items);
        }
    }

    private boolean isRegistered(String username) throws RemoteException, SQLException {
        User clientUser = SQLManager.getUser(username);
        if (clientUser!=null)
            return true;
        else
            return false;
    }

    

    

    

    private void registerClientsInterestForPriceRange(Client client, Integer priceRange) {
        requestedPriceRanges.put(priceRange, client);
    }

    // Getters
    public HashMap<String, Client> getUserMap() {
        return userMap;
    }

    
    
    @Override
    public synchronized void register(Client client,String username, String password) throws RemoteException {
        try {
            if (!isRegistered(username)) {
                try {
                    //registerClient(client);
                    User user = SQLManager.getUser(username);
                    if (user == null)
                        SQLManager.registerUser(username,password);
                    else 
                        System.out.println("Already registered user just tried to register");
                    
                } catch (SQLException ex) {
                    Logger.getLogger(ServerImpl.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        } catch (SQLException ex) {
            Logger.getLogger(ServerImpl.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    

    @Override
    public synchronized void publishItemOnMarket(Client client, Item item) throws RemoteException {
        
        //if client is logged       
        if (userMap.containsValue(client)) {
            try {
                // Add the Item to the market
                //AddItemToListOfAvailableItems(item);
                item.setSeller(client.getID());
                item.setIsForSale(true);
                
                
                SQLManager.registerItem(item);
            } catch (SQLException ex) {
                Logger.getLogger(ServerImpl.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

    }

    

    public void broadcastUpdates() throws RemoteException {
        for (Client client : userMap.values()) {
            
            //do something
        }
    }
    
    
    
   

    @Override
    public void buyItem(Client client, String itemName) throws RemoteException{
        try {
            Item item=SQLManager.getItem(itemName);
            buyItem(client, item);
        } catch (SQLException ex) {
            Logger.getLogger(ServerImpl.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    public void buyItem(Client client, Item item) throws RemoteException {
        try {
            client.withdraw(item.getPrice());
        } catch (RejectedException ex) {
            Logger.getLogger(ServerImpl.class.getName()).log(Level.SEVERE, null, ex);
            return;
        }
        
        
        Client seller=userMap.get(item.getSeller());//this function gives back the Client object of the seller
        try {
            seller.deposit(item.getPrice());
        } catch (RejectedException ex) {
            Logger.getLogger(ServerImpl.class.getName()).log(Level.SEVERE, null, ex);
            try {
                client.deposit(item.getPrice()); //refund money
                return;
            } catch (RejectedException ex1) {
                Logger.getLogger(ServerImpl.class.getName()).log(Level.SEVERE, null, ex1);
            }
        }
        
        
        
        
        
        try {
            item.setBuyer(client.getID());
            item.setIsForSale(false);
            try {
                SQLManager.updateItem(item);
            } catch (SQLException ex) {
                Logger.getLogger(ServerImpl.class.getName()).log(Level.SEVERE, null, ex);
            }
           
            
            
        } catch (RemoteException ex) {
            Logger.getLogger(ServerImpl.class.getName()).log(Level.SEVERE, null, ex);
        }
        
    }

    @Override
    public boolean logIn(Client client, String username, String password) throws RemoteException {
        
        System.out.println("trying to login");
        
        User clientUser=null;
        try {
            clientUser = SQLManager.getUser(username);
            
        } catch (SQLException ex) {
            Logger.getLogger(ServerImpl.class.getName()).log(Level.SEVERE, null, ex);
        }
        boolean result=false;
        if (clientUser != null) {
            if (clientUser.password.equals(password)){
            // Logged in users 
            
            userMap.put(username, client);
            System.out.println(userMap.get(username)+" just logged in");
            return true;
        }else 
            return false;
        }
        System.out.println("Non-registered user just tried to login");
        return false;   
    }

    @Override
    public void showInterestForPriceRange(Client client, Item item, Integer priceRange) throws RemoteException {
        
    }

    @Override
    public void updateItem(Client client, Item item) throws RemoteException {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public void listCurrentlyAvailableItems(Client client) throws RemoteException {
        try {
            client.serverHasItemsAvailable(SQLManager.getItems());
        } catch (SQLException ex) {
            Logger.getLogger(ServerImpl.class.getName()).log(Level.SEVERE, null, ex);
        }
        
    }

    @Override
    public void unregister(Client client) throws RemoteException {
        throw new UnsupportedOperationException("Not supported yet.");
    }
}
